// Copyright (C) 1953-2020 NUDT
// Verilog module name - packet_descriptor_send
// Version: DES_V1.0
// Created:
//         by - fenglin 
//         at - 10.2020
////////////////////////////////////////////////////////////////////////////
// Description:
//         Descriptor Send
///////////////////////////////////////////////////////////////////////////

`timescale 1ns/1ps

module packet_descriptor_send#(parameter forward_mode = 1'b0)//0:cutthrough; 1:stored  
(
        i_clk,
        i_rst_n,
        
        i_pkt_write_finish,
        iv_free_bufid_num,
        iv_hpriority_be_threshold_value,
        iv_rc_threshold_value,
        iv_lpriority_be_threshold_value,
        ov_traffic_police_discard_cnt,        
        
        i_descriptor_valid,
        iv_descriptor, 
        iv_eth_type, 
        iv_tsmp_subtype,        
        i_pkt_bufid_wr,
        iv_pkt_bufid,
        o_pkt_bufid_ack,
        
        o_pkt_bufid_wr,
        ov_pkt_bufid,
        ov_descriptor        ,
        o_descriptor_wr      ,
        ov_eth_type          ,
        ov_tsmp_subtype      ,        
        
        descriptor_send_state
    );

// I/O
// clk & rst
input                   i_clk;
input                   i_rst_n;
//input
input                   i_pkt_write_finish   ;
input       [8:0]       iv_free_bufid_num    ;
input       [8:0]       iv_rc_threshold_value;
input       [8:0]       iv_hpriority_be_threshold_value;
input       [8:0]       iv_lpriority_be_threshold_value;
output reg  [31:0]      ov_traffic_police_discard_cnt  ;

input                   i_descriptor_valid;
input       [11:0]      iv_descriptor;
input       [15:0]      iv_eth_type;
input       [7:0]       iv_tsmp_subtype;
input                   i_pkt_bufid_wr;
input       [8:0]       iv_pkt_bufid;
output  reg             o_pkt_bufid_ack;
//output
output  reg             o_pkt_bufid_wr;
output  reg [8:0]       ov_pkt_bufid;

output  reg [11:0]      ov_descriptor        ;
output  reg             o_descriptor_wr  ;
output  reg [15:0]      ov_eth_type ;
output  reg [7:0]       ov_tsmp_subtype;

output  reg [2:0]       descriptor_send_state;
//temp ov_descriptor and ov_pkt for discarding pkt while the fifo_used_findows is over the threshold 
//internal wire&reg
reg         [5:0]  rv_cycle_cnt;
localparam  IDLE_S                = 3'd0,
            DELAY_S               = 3'd1,
            WAIT_PKT_RX_FINISH_S  = 3'd2;
        
always@(posedge i_clk or negedge i_rst_n)
    if(!i_rst_n) begin
        o_pkt_bufid_ack         <= 1'b0;
        o_pkt_bufid_wr          <= 1'b0;
        ov_pkt_bufid            <= 9'b0;        

        ov_descriptor           <= 12'b0;
        o_descriptor_wr         <= 1'b0 ;
        ov_eth_type             <= 16'b0 ;
        ov_tsmp_subtype         <= 8'b0;

        ov_traffic_police_discard_cnt <= 32'b0;        

        rv_cycle_cnt            <= 6'b0;
        descriptor_send_state   <= IDLE_S;
    end
    else begin
        case(descriptor_send_state)
            IDLE_S:begin
                rv_cycle_cnt            <= 6'b0;
                o_descriptor_wr         <= 1'b0;
                ov_eth_type             <= iv_eth_type ;
                ov_tsmp_subtype         <= iv_tsmp_subtype;                
                if(i_pkt_bufid_wr == 1'b1 && i_descriptor_valid == 1'b1)begin//when descriptor come,pkt_bufid_wr have been already
                    if(iv_eth_type == 16'h8100)begin//tsn pkt
                        if(iv_descriptor[11:9] >= 3'h5)begin//st
                            if(iv_free_bufid_num == 9'h0)begin
                            //discard pkt when bufid under threshold or bufid is used up
                                ov_traffic_police_discard_cnt          <= ov_traffic_police_discard_cnt + 1'b1;
                                o_pkt_bufid_ack                        <= 1'b0;
                                descriptor_send_state                  <= IDLE_S;
                            end
                            else begin
                                o_pkt_bufid_wr        <= i_pkt_bufid_wr;
                                ov_pkt_bufid          <= iv_pkt_bufid;
                                ov_descriptor         <= {iv_descriptor[11:9],iv_pkt_bufid}; 
                                o_pkt_bufid_ack       <= 1'b1;                                    
                                descriptor_send_state <= DELAY_S;                                       
                            end
                        end
                        else begin//rc
                            if((iv_free_bufid_num <= iv_rc_threshold_value) || (iv_free_bufid_num == 9'h0))begin//DISCARD
                            //discard pkt when bufid under threshold or bufid is used up
                                ov_traffic_police_discard_cnt          <= ov_traffic_police_discard_cnt + 1'b1;
                                o_pkt_bufid_ack                        <= 1'b0;
                                descriptor_send_state                  <= IDLE_S;
                            end
                            else begin
                                o_pkt_bufid_wr        <= i_pkt_bufid_wr;
                                ov_pkt_bufid          <= iv_pkt_bufid;
                                ov_descriptor         <= {iv_descriptor[11:9],iv_pkt_bufid};                                   
                                
                                o_pkt_bufid_ack       <= 1'b1;
                                descriptor_send_state <= DELAY_S;                                         
                            end
                        end
                    end
                    else begin//standard ethernet pkt
                        if(iv_eth_type == 16'hff01)begin//tsmp
                            if((iv_free_bufid_num <=  iv_hpriority_be_threshold_value)||(iv_free_bufid_num <=  iv_rc_threshold_value)|| (iv_free_bufid_num == 9'h0)) begin
                            //discard pkt when bufid under threshold or bufid is used up
                                ov_traffic_police_discard_cnt              <= ov_traffic_police_discard_cnt + 1'b1;
                                o_pkt_bufid_ack                            <= 1'b0;
                                descriptor_send_state                      <= IDLE_S;
                            end 
                            else begin
                                o_pkt_bufid_wr        <= i_pkt_bufid_wr;
                                ov_pkt_bufid          <= iv_pkt_bufid;
                                ov_descriptor         <= {iv_descriptor[11:9],iv_pkt_bufid};

                                o_pkt_bufid_ack       <= 1'b1;                                
                                descriptor_send_state <= DELAY_S;                                          
                            end
                        end
                        else begin 
                            if((iv_free_bufid_num <=  iv_lpriority_be_threshold_value) || (iv_free_bufid_num <=  iv_hpriority_be_threshold_value)||(iv_free_bufid_num <=  iv_rc_threshold_value)||(iv_free_bufid_num == 9'h0)) begin
                            //discard pkt when bufid under threshold or bufid is used up
                                ov_traffic_police_discard_cnt              <= ov_traffic_police_discard_cnt + 1'b1;
                                o_pkt_bufid_ack                            <= 1'b0;
                                descriptor_send_state                      <= IDLE_S;
                            end 
                            else begin
                                o_pkt_bufid_wr        <= i_pkt_bufid_wr;
                                ov_pkt_bufid          <= iv_pkt_bufid;
                                ov_descriptor         <= {iv_descriptor[11:9],iv_pkt_bufid};

                                o_pkt_bufid_ack       <= 1'b1;                                
                                descriptor_send_state <= DELAY_S;                                    
                            end
                        end
					end
                end
                else if(i_pkt_bufid_wr == 1'b0 && i_descriptor_valid == 1'b1)begin
                    o_pkt_bufid_ack             <= 1'b0;
                    o_pkt_bufid_wr              <= 1'b0;
                    ov_pkt_bufid                <= 9'b0;

                    ov_descriptor               <= 12'b0;
                    descriptor_send_state       <= IDLE_S;  
                end
                else begin
                    o_pkt_bufid_ack       <= 1'b0;
                    o_pkt_bufid_wr        <= 1'b0;
                    ov_pkt_bufid          <= 9'b0;
    
                    ov_descriptor         <= 12'b0;
                    
                    descriptor_send_state       <= IDLE_S;
                end
            end
            DELAY_S:begin
                o_pkt_bufid_wr  <= 1'b0;
                ov_pkt_bufid    <= 9'b0;   
                o_pkt_bufid_ack <= 1'b0;
                rv_cycle_cnt    <= rv_cycle_cnt + 1'b1;
                if(rv_cycle_cnt == 6'd30)begin                 
                    if(forward_mode)begin//1:stored    
                        o_descriptor_wr         <= 1'b0;
                        descriptor_send_state   <= WAIT_PKT_RX_FINISH_S;
                    end
                    else begin//0:cutthrough    
                        o_descriptor_wr         <= 1'b1;
                        descriptor_send_state   <= IDLE_S;
                    end
                end
                else begin
                    o_descriptor_wr         <= 1'b0;
                    descriptor_send_state   <= DELAY_S;
                end
            end
            WAIT_PKT_RX_FINISH_S:begin
                if(i_pkt_write_finish)begin
                    o_descriptor_wr         <= 1'b1;
                    descriptor_send_state   <= IDLE_S;
                end
                else begin
                    o_descriptor_wr         <= 1'b0;
                    descriptor_send_state   <= WAIT_PKT_RX_FINISH_S;
                end
            end            
            default:begin
                o_pkt_bufid_ack         <= 1'b0;
                o_pkt_bufid_wr          <= 1'b0;
                ov_pkt_bufid            <= 9'b0;        
                
                ov_descriptor           <= 12'b0;
                o_descriptor_wr         <= 1'b0;              

                descriptor_send_state   <= IDLE_S;
            end
        endcase
    end
endmodule